package com.autonavi.yunda.yunji.service;

import com.autonavi.yunda.yunji.common.exception.AmapPreconditions;
import com.autonavi.yunda.yunji.common.utils.JsonUtils;
import com.autonavi.yunda.yunji.core.enums.InterfaceStatus;
import com.autonavi.yunda.yunji.core.model.condition.DynamicConfigQueryCondition;
import com.autonavi.yunda.yunji.core.model.condition.InterfaceQueryCondition;
import com.autonavi.yunda.yunji.core.model.dto.*;
import com.autonavi.yunda.yunji.core.service.*;
import com.autonavi.yunda.yunji.core.utils.ExportUtil;
import com.autonavi.yunda.yunji.core.vo.localization.*;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletResponse;
import java.util.Collections;
import java.util.List;
import java.util.stream.Collectors;

/**
 * @description 本地化/离线导出方案
 * @author <a href="mailto:yanxudong.yxd@alibaba-inc.com">Send Email 2 阎旭东(观清)</a>
 * @date 2023/9/6
 * @version 1.0
 */
@Slf4j
@Service
@RequiredArgsConstructor
public class LocalizationService {

    @Resource
    private final ProjectService projectService;
    @Resource
    private final InterfaceConfigService interfaceConfigService;
    @Resource
    private final DatasourceProjectMappingService datasourceProjectConfigService;
    @Resource
    private final DynamicConfigService dynamicConfigService;
    @Resource
    private final FunctionProjectMappingService functionProjectConfigService;

    private final String EXPORT_INTERFACE = "interface";
    private final String EXPORT_DATASOURCE = "datasource";
    private final String EXPORT_DYNAMIC = "dynamic";
    private final String EXPORT_FUNCTION = "function";

    /**
     * 本地化/离线方案-导出指定项目的文件(interface/dataSource/dynamic/function)
     * @param projectInfo   项目信息
     * @param response      http response
     * @return 导出是否成功
     */
    public boolean exportLocalizationFile(ProjectDTO projectInfo, HttpServletResponse response) {
        // 检查并提取导出信息
        LocalizationInfoVO exportInfo = checkNFetchExportInfo(projectInfo.getProjectId());

        // 导出零散文件
        String rootPath = downloadLocalizationInfo(projectInfo, exportInfo);

        // 压缩打包+触发浏览器下载
        ExportUtil.zipNTrans(rootPath, projectInfo.getName(), response);

        // 清空导出文件，释放存储空间
        log.info("before delete file list:{}", ExportUtil.fetchFolderInfoByPath(rootPath));
        ExportUtil.clearFileByPath(rootPath);
        log.info("after delete file list:{}", ExportUtil.fetchFolderInfoByPath(rootPath));
        return true;
    }

    /**
     * 下载文件至临时目录
     * @param projectInfo   项目信息
     * @param exportInfo    导出主体信息
     * @return   临时目录
     */
    private String downloadLocalizationInfo(ProjectDTO projectInfo, LocalizationInfoVO exportInfo) {
        String rootPath = ExportUtil.fetchExportRootPath() + "/";
        exportInfo.getInterfaceConfig().forEach((interfaceItem) ->
                ExportUtil.export(rootPath, projectInfo.getName(), EXPORT_INTERFACE, interfaceItem.getName(), JsonUtils.toStringFormat(interfaceItem)));
        exportInfo.getDatasourceConfig().forEach((datasource) ->
                ExportUtil.export(rootPath, projectInfo.getName(), EXPORT_DATASOURCE, datasource.getDbname(), JsonUtils.toStringFormat(datasource)));
        exportInfo.getDynamicConfig().forEach((dynamic) ->
                ExportUtil.export(rootPath, projectInfo.getName(), EXPORT_DYNAMIC, dynamic.getKeyName(), JsonUtils.toStringFormat(dynamic)));
        exportInfo.getFunctionConfig().forEach((function) ->
                ExportUtil.export(rootPath, projectInfo.getName(), EXPORT_FUNCTION, function.getName(), JsonUtils.toStringFormat(function)));
        return rootPath;
    }

    /**
     * 检查导出信息是否符合本地化要求，并提取出符合要求的内容
     * @param projectId 项目id
     */
    private LocalizationInfoVO checkNFetchExportInfo(String projectId) {
        // 接口校验&获取
        List<InterfaceExportVO> interfaceList = checkNFetchInterfaces(projectId);
        // 数据源校验&获取
        List<DatasourceExportVO> datasourceList = checkNFetchDatasources(projectId);
        // 动态数据获取
        List<DynamicExportVO> dynamicList = fetchDynamics(projectId);
        // 函数获取
        List<FunctionExportVO> functionList = fetchFunctions(projectId);

        return LocalizationInfoVO.builder()
                .interfaceConfig(interfaceList)
                .datasourceConfig(datasourceList)
                .dynamicConfig(dynamicList)
                .functionConfig(functionList)
                .build();
    }

    /**
     * 获取函数列表
     * @param projectId 项目id
     * @return    接口列表
     */
    private List<FunctionExportVO> fetchFunctions(String projectId) {
        List<FunctionConfigDTO> functionList = functionProjectConfigService.listFunctionByProjectId(projectId);

        return functionList.stream().map(item -> FunctionExportVO.builder()
                .name(item.getName())
                .params(item.getParams())
                .description(item.getDescription())
                .type(item.getType())
                .status(item.getStatus())
                .version(item.getVersion())
                .functionScript(item.getFunctionScript())
                .build()).collect(Collectors.toList());
    }

    /**
     * 获取动态数据
     * @param projectId 项目id
     * @return   动态数据
     */
    private List<DynamicExportVO> fetchDynamics(String projectId) {
        DynamicConfigQueryCondition dynamicQuery = DynamicConfigQueryCondition.builder().projectId(projectId).build();
        List<DynamicConfigDTO> dynamicList = dynamicConfigService.listAll(dynamicQuery);
        if (dynamicList.size() == 0) {
            return Collections.emptyList();
        }
        return dynamicList.stream().map(item -> DynamicExportVO.builder()
                .projectId(item.getProjectId())
                .keyName(item.getKeyName())
                .value(item.getValue())
                .description(item.getDescription())
                .build()).collect(Collectors.toList());
    }

    /**
     * 检查并获取合适的数据源列表
     * @param projectId 项目id
     * @return  数据源列表
     */
    private List<DatasourceExportVO> checkNFetchDatasources(String projectId) {
        List<DatasourceConfigDTO> datasourceList = datasourceProjectConfigService.listDatasourceByProjectIds(Collections.singletonList(projectId));
//        datasourceList.forEach(ds -> AmapPreconditions.checkArgumentForUser(DatasourceType.canExportType(ds.getType()), "该项目不支持导出的数据源:" + ds.getType()));

        return datasourceList.stream().map(item -> DatasourceExportVO.builder()
                    .dbname(item.getDbname())
                    .description(item.getDescription())
                    .type(item.getType())
                    .config(item.getConfig())
                    .build()).collect(Collectors.toList());
    }

    /**
     * 检查并获取合适的接口列表
     * @param projectId 项目id
     * @return  接口列表
     */
    private List<InterfaceExportVO> checkNFetchInterfaces(String projectId) {
        // 接口校验&获取
        InterfaceQueryCondition queryCondition = InterfaceQueryCondition.builder()
                .projectId(projectId)
                .status(InterfaceStatus.PUBLISHED)
                .build();
        List<InterfaceConfigDTO> configList = interfaceConfigService.list(queryCondition);
        AmapPreconditions.checkArgumentForUser(configList.size() != 0, "该项目不存在已上线的接口,不产生导出内容");

        return configList.stream().map((item) -> InterfaceExportVO.builder()
                    .name(item.getName())
                    .uri(item.getUri())
                    .description(item.getDescription())
                    .method(item.getMethod())
                    .version(item.getVersion())
                    .projectId(item.getProjectId())
                    .engineData(item.getEngineData())
                    .engineType(item.getEngineType())
                    .build()).collect(Collectors.toList());
    }

    /**
     * 导出项目下的文件，包含接口/数据源/动态变量/函数等
     * @param projectId 项目id
     * @param response  http response
     * @return    导出结果
     */
    public String exportLocalizationFileByProjectId(String projectId, HttpServletResponse response) {
        ProjectDTO dto = projectService.findByProjectIdOrThrow(projectId);
        return this.exportLocalizationFile(dto, response) ? "success" : "fail";
    }
}
